package app

import (
	"errors"
	"os"
	"os/signal"
	"sync/atomic"
	"syscall"

	util "gitee.com/fanshuaiself/sidecar/util"
	log "github.com/sirupsen/logrus"
)

type Plugin interface {
	Initialize(config *util.Config, closeFunc func())
	Start() error
	Stop ()
}

type App struct {
	config *util.Config
	plugins []Plugin
	started int32
	running int32
	closeCh chan int
}

func NewApp(config *util.Config, plugins []Plugin) *App {
	app := App {
		config: config,
		plugins: make([]Plugin, len(plugins)),
		closeCh: make(chan int, 1),
	}

	app.plugins = append(app.plugins, plugins...)
	return &app
}

func (app *App) Start() error {
	if !atomic.CompareAndSwapInt32(&app.started, 0, 1) {
		return errors.New("app has started before")
	}
	if len(app.plugins) == 0 {
		return errors.New("plugins empty")
	}

	idx := 0
	var err error 
	for idx = range app.plugins {
		plugin := app.plugins[idx]
		plugin.Initialize(app.config, app.Stop)

		if err = plugin.Start(); err != nil {
			break
		}
	}

	if err != nil {
		for i := 0; i < idx; i++ {
			app.plugins[i].Stop()
		}

		return err
	}

	app.running = 1

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGUSR1)

	select {
	case <- ch:
		break
	case <- app.closeCh:
		log.Println("监听到退出事件")
		break
	}

	for _, plugin := range app.plugins {
		plugin.Stop()
	}
	return nil
}

func (app * App) Stop() {
	app.closeCh <- 1
}


